其他
Windows内核-句柄
本文为看雪论坛精华文章
看雪论坛作者ID:SnA1lGo
概述
句柄
相关结构体:
//0x3c bytes (sizeof)
struct _HANDLE_TABLE
{
ULONG TableCode; //0x0
//指向句柄表的存储结构(非常重要的字段)
struct _EPROCESS* QuotaProcess; //0x4
//句柄表的内存资源存储在此进程中
VOID* UniqueProcessId; //0x8
//创建进程的ID,用于回调函数
struct _EX_PUSH_LOCK HandleLock; //0xc
//句柄表锁,仅在句柄表扩展时使用
struct _LIST_ENTRY HandleTableList; //0x10
//所有的句柄表形成一个链表,该字段作为一个链表节点
//链表头为全局变量HandleTableListHead
struct _EX_PUSH_LOCK HandleContentionEvent; //0x18
//访问句柄时发生竞争,就通过该推锁进行等待
struct _HANDLE_TRACE_DEBUG_INFO* DebugInfo; //0x1c
//仅当使用调试句柄时才有意义
LONG ExtraInfoPages; //0x20
//审计信息所占用的页面数量
union
{
ULONG Flags; //0x24
//标志域
UCHAR StrictFIFO:1; //0x24
//是否使用队列的风格,FIFO先进先出,先释放的地方先使用。
};
ULONG FirstFreeHandle; //0x28
//当前句柄表中的空闲句柄表项的索引值
//句柄索引值按HANDLE_VALUE_INC逐个递增,在win7 sp1 32位中为4字节
struct _HANDLE_TABLE_ENTRY* LastFreeHandleEntry; //0x2c
//当前句柄表中最后一个空闲句柄表项的地址
ULONG HandleCount; //0x30
//正在使用的句柄表项数量
ULONG NextHandleNeedingPool; //0x34
//下一次句柄表扩展的起始句柄索引,也就是下一个新的句柄表的首地址
ULONG HandleCountHighWatermark; //0x38
};
//0x8 bytes (sizeof)
struct _HANDLE_TABLE_ENTRY
{
union
{
VOID* Object; //0x0
ULONG ObAttributes; //0x0
struct _HANDLE_TABLE_ENTRY_INFO* InfoTable; //0x0
ULONG Value; //0x0
};
union
{
ULONG GrantedAccess; //0x4
struct
{
USHORT GrantedAccessIndex; //0x4
USHORT CreatorBackTraceIndex; //0x6
};
ULONG NextFreeTableEntry; //0x4
};
};
/*
该结构体后续再解释 目前只需知道该结构体的低32位到低2位保存的是内核对象的首地址
以下在结构体中的低地址的union的32-2位中保存着首地址
union
{
VOID* Object; //0x0
ULONG ObAttributes; //0x0
struct _HANDLE_TABLE_ENTRY_INFO* InfoTable; //0x0
ULONG Value; //0x0
};
例如:_handle_table_entry == 00000001`8812ad09
那么对应的对象首地址就为 8812ad09将低3位清零的结果:8812ad08
*/
句柄表
句柄表产生流程:
TableCode字段
句柄通过句柄表得到内核对象的流程:
通过私有句柄表:
通过公有句柄表:
Windbg查看
私有句柄表
kd> !process 0 0
PROCESS 86e97d20 SessionId: 1 Cid: 03e8 Peb: 7ffdf000 ParentCid: 0594
DirBase: 07f70000 ObjectTable: a79b91c0 HandleCount: 72.
Image: notepad++.exe
// 首地址为:a79b91c0
kd> dt _HANDLE_TABLE a79b91c0
ntdll!_HANDLE_TABLE
+0x000 TableCode : 0x8b4a0000
+0x004 QuotaProcess : 0x86e97d20 _EPROCESS
+0x008 UniqueProcessId : 0x000003e8 Void
+0x00c HandleLock : _EX_PUSH_LOCK
+0x010 HandleTableList : _LIST_ENTRY [ 0x84145e28 - 0xa87854f8 ]
+0x018 HandleContentionEvent : _EX_PUSH_LOCK
+0x01c DebugInfo : (null)
+0x020 ExtraInfoPages : 0n0
+0x024 Flags : 0
+0x024 StrictFIFO : 0y0
+0x028 FirstFreeHandle : 0xcc
+0x02c LastFreeHandleEntry : 0x8b4a0ff8 _HANDLE_TABLE_ENTRY
+0x030 HandleCount : 0x48
+0x034 NextHandleNeedingPool : 0x800
+0x038 HandleCountHighWatermark : 0x4b
0x8b4a0000 的前四位为: 1000
表明只有一层句柄表,
清零后得到句柄表的首地址:
0x8b4a0000
4.1 如果只有一层就是TableCode前30位的值
4.2 如果有两层就需要先将句柄值除以512,看看占满了多少个最底层句柄表,然后将在TableCode中找到前面占满了的最底层句柄表的首地址的存放地址,再后面一个就是对应的最底层句柄表了。然后将句柄值-占满句柄表的个数*512等到在对应的最底层句柄表中句柄的偏移值,然后将该值*2得到句柄表项在句柄表中的偏移值。
4.3 和4.2类似。
这里的情况就是4.1,直接可以得到对应表的地址为0x8b4a0000
句柄表中存放的是句柄项,句柄项是一个结构体里面包含了句柄值:
//0x8 bytes (sizeof)
struct _HANDLE_TABLE_ENTRY
{
union
{
VOID* Object; //0x0
//指向句柄代表的对象
ULONG ObAttributes; //0x0
struct _HANDLE_TABLE_ENTRY_INFO* InfoTable; //0x0
ULONG Value; //0x0
};
union
{
ULONG GrantedAccess; //0x4
struct
{
USHORT GrantedAccessIndex; //0x4
USHORT CreatorBackTraceIndex; //0x6
};
ULONG NextFreeTableEntry; //0x4
};
};
因为句柄表项结构大小为8字节,而句柄的大小为4字节,所以在得到句柄表中句柄表项的偏移时,还需要将对应句柄表的句柄值*2。
例如这里的句柄表值为0x28,那么对应到句柄表中的偏移为0x28*2
//通过句柄表+偏移值的方式得到句柄表项中的对象地址,然后将后三位清零后得到对象的头地址,
//然后将头地址往下偏移就可以得到对象首地址
这里查看前面图中句柄值为0x28的内容:
kd> dq 0x8b4a0000+0x28*2
8b4a0050 000f01ff`87b3f329 000f037f`87b3ea41
8b4a0060 00020019`a7a133c9 00000001`a8795619
8b4a0070 00000804`86e6b0d9 00000804`86de5291
8b4a0080 00000804`88002da1 00000804`86de0b99
8b4a0090 00000804`87cb48d1 00000804`88002c39
8b4a00a0 00000804`86cee6f9 00000804`880bb271
8b4a00b0 00000804`86db1779 001f0001`87c722a9
8b4a00c0 001f0003`87bccf21 001f0001`87c46831
得到句柄表项中的对象值为87b3f329
将前三位清零后为:
87b3f328
往下偏移0x18后为:
87b3f340
查看对象内容:
kd> !object 87b3f340
Object: 87b3f340 Type: (866f67a0) Desktop
ObjectHeader: 87b3f328 (new version)
HandleCount: 12 PointerCount: 689
Directory Object: 00000000 Name: Default
公有句柄表:
PROCESS 866d2900 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00185000 ObjectTable: 8b401b28 HandleCount: 430.
Image: System
System的句柄表地址为;8b401b28
kd> dd PspCidTable
8418fd54 8b401080 00000000 80000020 00000101
全局句柄表地位为:8b401080
公有句柄表和私有句柄表的区别:
查看公有句柄表:
kd> dd PspCidTable
8414cd54 8b401080 00000000 80000020 00000101
kd> dt _handle_table 8b401080
ntdll!_HANDLE_TABLE
+0x000 TableCode : 0xa3aec001
+0x004 QuotaProcess : (null)
+0x008 UniqueProcessId : (null)
+0x00c HandleLock : _EX_PUSH_LOCK
+0x010 HandleTableList : _LIST_ENTRY [ 0x8b401090 - 0x8b401090 ]
+0x018 HandleContentionEvent : _EX_PUSH_LOCK
+0x01c DebugInfo : (null)
+0x020 ExtraInfoPages : 0n0
+0x024 Flags : 1
+0x024 StrictFIFO : 0y1
+0x028 FirstFreeHandle : 0xa1c
+0x02c LastFreeHandleEntry : 0xa3ae4430 _HANDLE_TABLE_ENTRY
+0x030 HandleCount : 0x272
+0x034 NextHandleNeedingPool : 0x1000
+0x038 HandleCountHighWatermark : 0x275
//TableCode后三位为1,说明有两层。
kd> dd 0xa3aec000
a3aec000 8b404000 a3ae4000 00000000 00000000
a3aec010 00000000 00000000 00000000 00000000
a3aec020 00000000 00000000 00000000 00000000
//数组中只有前两个有值,说明最底层句柄表只有两个
//查看第一个表的内容:
kd> dq 8b404000
8b404000 fffffffe`00000000 00000000`866d2901
8b404010 00000000`866d2629 00000000`866f1a09
8b404020 00000000`866f5c81 00000000`866e6021
8b404030 00000000`866e6d49 00000000`866e6a71
8b404040 00000000`866e6799 00000000`866e64c1
8b404050 00000000`866e7021 00000000`866e7d49
8b404060 00000000`866e7a71 00000000`866e7799
8b404070 00000000`866e74c1 00000000`866e8021
//随便选个句柄表项查看对应的内核对象:
kd> dt _handle_table_entry 8b404050
ntdll!_HANDLE_TABLE_ENTRY
+0x000 Object : 0x866e7021 Void
+0x000 ObAttributes : 0x866e7021
+0x000 InfoTable : 0x866e7021 _HANDLE_TABLE_ENTRY_INFO
+0x000 Value : 0x866e7021
+0x004 GrantedAccess : 0
+0x004 GrantedAccessIndex : 0
+0x006 CreatorBackTraceIndex : 0
+0x004 NextFreeTableEntry : 0
//将object前三位清零查看对应的内核对象:
kd> !object 0x866e7020
Object: 866e7020 Type: (866d2de8) Thread
ObjectHeader: 866e7008 (new version)
HandleCount: 0 PointerCount: 1
句柄的权限
struct _HANDLE_TABLE_ENTRY
{
union
{
VOID* Object; //0x0
ULONG ObAttributes; //0x0
struct _HANDLE_TABLE_ENTRY_INFO* InfoTable; //0x0
ULONG Value; //0x0
};
union
{
ULONG GrantedAccess; //0x4
struct
{
USHORT GrantedAccessIndex; //0x4
USHORT CreatorBackTraceIndex; //0x6
};
ULONG NextFreeTableEntry; //0x4
};
};
#include <windows.h>
#include<iostream>
using namespace std;
int main()
{
cout << "test" << endl;
DWORD tempId;
cin >> tempId;
auto tempHandle = OpenProcess(PROCESS_ALL_ACCESS, 0,tempId);
if (tempHandle == NULL)
{
cout << "打开进程失败" << endl;
return 0;
}
CloseHandle(tempHandle);
return 0;
}
kd> !process 0 0
PROCESS 86906d20 SessionId: 1 Cid: 053c Peb: 7ffdf000 ParentCid: 0a3c
DirBase: 33575000 ObjectTable: a6caa668 HandleCount: 9.
Image: ApplicationTest1.exe //你的程序名称
kd> dt _handle_table a6caa668
ntdll!_HANDLE_TABLE
+0x000 TableCode : 0x88608000
+0x004 QuotaProcess : 0x86906d20 _EPROCESS
+0x008 UniqueProcessId : 0x0000053c Void
+0x00c HandleLock : _EX_PUSH_LOCK
+0x010 HandleTableList : _LIST_ENTRY [ 0x8859c788 - 0x8842c8a8 ]
+0x018 HandleContentionEvent : _EX_PUSH_LOCK
+0x01c DebugInfo : (null)
+0x020 ExtraInfoPages : 0n0
+0x024 Flags : 0
+0x024 StrictFIFO : 0y0
+0x028 FirstFreeHandle : 0x28
+0x02c LastFreeHandleEntry : 0x88608ff8 _HANDLE_TABLE_ENTRY
+0x030 HandleCount : 9
+0x034 NextHandleNeedingPool : 0x800
+0x038 HandleCountHighWatermark : 0xa
kd> dq 0x88608000+0x24*0x2
88608048 001fffff`88175969 0000002c`00000000
88608058 00000030`00000000 00000034`00000000
88608068 00000038`00000000 0000003c`00000000
88608078 00000040`00000000 00000044`00000000
88608088 00000048`00000000 0000004c`00000000
88608098 00000050`00000000 00000054`00000000
886080a8 00000058`00000000 0000005c`00000000
886080b8 00000060`00000000 00000064`00000000
(56-64bit)8位:
#include <windows.h>
#include<iostream>
using namespace std;
int main()
{
cout << "test" << endl;
DWORD tempId;
cin >> tempId;
auto tempHandle = OpenProcess(0x0001, 0,tempId);
if (tempHandle == NULL)
{
cout << "打开进程失败" << endl;
return 0;
}
SetHandleInformation(tempHandle, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
CloseHandle(tempHandle);
return 0;
}
kd> dq 0x8864a000+0x24*0x2
8864a048 00000001`88175969 0000002c`00000000
8864a058 00000030`00000000 00000034`00000000
8864a068 00000038`00000000 0000003c`00000000
8864a078 00000040`00000000 00000044`00000000
kd> dq 0x8864a000+0x24*0x2
8864a048 02000001`88175969 0000002c`00000000
8864a058 00000030`00000000 00000034`00000000
(3-31bit)29位:
(0-3bit)3位
全局句柄表
NTSTATUS
PspCreateProcess(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess OPTIONAL,
IN ULONG Flags,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN ULONG JobMemberLevel
)
{
...
Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry);
...
}
由于保存的是body地址,所以在内核中,根据进程或线程的唯一ID值,可以很快的找到对应的内核对象,例如以下API:
句柄小结
参考资料
微软官方文档
借鉴博客
看雪ID:SnA1lGo
https://bbs.pediy.com/user-home-948248.htm
# 往期推荐
4.CVE-2022-21999 Windows Print Spooler 权限提升漏洞分析
6.The House of Mind (FASTBIN METHOD) + PRIME
球分享
球点赞
球在看
点击“阅读原文”,了解更多!